#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
启动脚本：同时启动前后端服务

使用方法：
    python start_server.py
"""

import os
import sys
import subprocess
import time
import signal
import socket

# 添加app路径到Python路径
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

from app.utils import (
    wait_for_service_start,
    terminate_process
)


def check_port_available(port):
    """
    检查端口是否可用
    
    参数:
        port: 要检查的端口号
    
    返回:
        bool: 端口可用返回True，否则返回False
    """
    try:
        # 创建socket对象
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置超时时间
        sock.settimeout(1)
        # 尝试绑定端口
        sock.bind(('localhost', port))
        # 关闭socket
        sock.close()
        return True
    except socket.error:
        return False


def check_npm_availability():
    """
    检查npm是否可用
    
    返回:
        bool: npm可用返回True，否则返回False
    """
    print("检查npm是否可用...")
    try:
        # 在Windows上使用shell=True确保能找到npm命令
        subprocess.run(["npm", "--version"], capture_output=True, check=True, shell=True)
        print("npm可用")
        return True
    except FileNotFoundError:
        print("错误: 未找到npm命令，请先安装Node.js并确保npm已添加到环境变量中")
        return False
    except subprocess.CalledProcessError:
        print("错误: npm命令执行失败")
        return False


def check_npm_dependencies(web_dir):
    """
    检查前端npm依赖是否已安装
    
    参数:
        web_dir: 前端项目目录路径
    
    返回:
        bool: 依赖已安装返回True，否则返回False
    """
    print("检查前端npm依赖...")
    node_modules_path = os.path.join(web_dir, "node_modules")
    if not os.path.exists(node_modules_path):
        print("前端依赖未安装，正在安装...")
        try:
            subprocess.run(["npm", "install"], cwd=web_dir, check=True, shell=True)
            print("前端依赖安装成功！")
            return True
        except subprocess.CalledProcessError:
            print("前端依赖安装失败，请检查网络连接或package.json文件")
            return False
    else:
        print("前端依赖已安装")
        return True


def install_python_dependencies():
    """
    安装后端Python依赖
    
    返回:
        bool: 安装成功返回True，否则返回False
    """
    print("安装后端Python依赖...")
    requirements_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "requirements", "dev.txt")
    try:
        subprocess.run(
            [sys.executable, "-m", "pip", "install", "-r", requirements_file],
            check=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )
        print("后端依赖安装成功！")
        return True
    except subprocess.CalledProcessError as e:
        print(f"后端依赖安装失败: {e.stderr}")
        return False


def check_python_dependencies():
    """
    检查后端Python依赖是否已安装，如未安装则自动安装
    
    返回:
        bool: 依赖检查/安装通过返回True，否则返回False
    """
    print("检查后端Python依赖...")
    try:
        # 尝试导入关键依赖
        import flask
        import sqlalchemy
        import flask_jwt_extended
        print("后端依赖检查通过")
        return True
    except ImportError as e:
        print(f"后端依赖缺失: {e}")
        return install_python_dependencies()


def start_backend():
    """
    启动Flask后端服务
    
    返回:
        subprocess.Popen: 后端进程对象
    """
    # 从环境变量读取后端端口，如未设置则使用默认值5000
    backend_port = int(os.getenv('BACKEND_PORT', 5000))
    if not check_port_available(backend_port):
        print(f"错误: 后端端口 {backend_port} 已被占用，请检查是否有其他服务在运行")
        return None
    
    print("启动后端Flask服务...")
    backend_process = subprocess.Popen(
        [sys.executable, "wsgi.py"],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        text=True
    )
    
    # 使用封装的工具函数等待服务启动
    if wait_for_service_start(backend_process, backend_port, "后端服务"):
        return backend_process
    else:
        return None


def start_frontend(web_dir):
    """
    启动Vite前端开发服务器
    
    参数:
        web_dir: 前端项目目录路径
    
    返回:
        subprocess.Popen: 前端进程对象
    """
    # 从环境变量读取前端端口，如未设置则使用默认值5228
    frontend_port = int(os.getenv('FRONTEND_PORT', 5228))
    
    print("启动前端Vite开发服务器...")
    # 在Windows上使用shell=True确保能找到npm命令
    frontend_process = subprocess.Popen(
        "npm run dev",
        cwd=web_dir,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        text=True,
        shell=True
    )
    
    # 使用封装的工具函数等待服务启动
    if wait_for_service_start(frontend_process, frontend_port, "前端服务"):
        return frontend_process
    else:
        return None


def print_service_info():
    """
    打印服务信息
    """
    # 从环境变量读取端口，如未设置则使用默认值
    frontend_port = int(os.getenv('FRONTEND_PORT', 5228))
    backend_port = int(os.getenv('BACKEND_PORT', 5000))
    
    print("\n" + "=" * 50)
    print("PyScreenBrain 服务已启动！")
    print(f"前端访问地址: http://localhost:{frontend_port} (默认进入登录页面)")
    print(f"后端API地址: http://localhost:{backend_port}")
    print("=" * 50)
    print("按 Ctrl+C 停止所有服务")


def stop_services(backend_process, frontend_process):
    """
    停止所有服务
    
    参数:
        backend_process: 后端进程对象
        frontend_process: 前端进程对象
    """
    print("\n停止服务中...")
    
    # 停止前端服务
    if frontend_process and frontend_process.poll() is None:
        frontend_process.terminate()
        try:
            frontend_process.wait(timeout=5)
        except subprocess.TimeoutExpired:
            frontend_process.kill()
    
    # 停止后端服务
    if backend_process and backend_process.poll() is None:
        backend_process.terminate()
        try:
            backend_process.wait(timeout=5)
        except subprocess.TimeoutExpired:
            backend_process.kill()
    
    print("所有服务已停止")


def handle_signal(signum, frame):
    """
    处理信号（如Ctrl+C）
    """
    global backend_process, frontend_process
    stop_services(backend_process, frontend_process)
    sys.exit(0)


def main():
    """
    主函数
    """
    global backend_process, frontend_process
    
    # 设置当前工作目录为项目根目录
    project_root = os.path.dirname(os.path.abspath(__file__))
    os.chdir(project_root)
    
    web_dir = os.path.join(project_root, "web")
    
    # 检查依赖
    if not check_python_dependencies():
        sys.exit(1)
    
    npm_available = check_npm_availability()
    frontend_enabled = True
    
    if not npm_available:
        print("\n警告：前端服务需要Node.js环境，当前无法启动前端")
        print("请安装Node.js并确保npm已添加到环境变量中以启用前端功能")
        print("Node.js下载地址：https://nodejs.org/")
        frontend_enabled = False
    else:
        if not check_npm_dependencies(web_dir):
            print("前端依赖安装失败，将只启动后端服务")
            frontend_enabled = False
    
    # 注册信号处理
    signal.signal(signal.SIGINT, handle_signal)
    signal.signal(signal.SIGTERM, handle_signal)
    
    # 启动服务
    backend_process = start_backend()
    frontend_process = None
    
    # 检查后端服务是否启动成功
    if backend_process is None:
        print("后端服务启动失败，无法继续启动前端服务")
        if frontend_process:
            frontend_process.terminate()
        sys.exit(1)
    
    if frontend_enabled:
        frontend_process = start_frontend(web_dir)
        # 检查前端服务是否启动成功
        if frontend_process is None:
            print("前端服务启动失败，将只运行后端服务")
            frontend_enabled = False
    
    # 打印服务信息
    print_service_info()
    
    if not frontend_enabled:
        print("注意：前端服务未启动")
        print("请安装Node.js并确保npm已添加到环境变量中，然后重新运行此脚本以启动完整服务")
    
    try:
        # 保持脚本运行
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        pass
    finally:
        stop_services(backend_process, frontend_process)


if __name__ == "__main__":
    backend_process = None
    frontend_process = None
    main()